package nacos

import (
	"fmt"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/config_client"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/model"
	"github.com/nacos-group/nacos-sdk-go/vo"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

// Client nacos客户端
type Client struct {
	signalChan   chan os.Signal
	mutex        sync.RWMutex
	Running      bool
	Config       *Config
	namingClient naming_client.INamingClient
	configClient config_client.IConfigClient

	serverConfigs []constant.ServerConfig
	clientConfig  constant.ClientConfig
	once          sync.Once
}

func (c *Client) initConfig() {
	c.once.Do(func() {
		ipAddr := strings.Split(c.Config.NacosServer, ":")[0]
		port, err := strconv.Atoi(strings.Split(c.Config.NacosServer, ":")[1])
		if err != nil {
			log.Panic(err)
		}

		c.serverConfigs = []constant.ServerConfig{
			{
				IpAddr: ipAddr,
				Port:   uint64(port),
			},
		}

		c.clientConfig = constant.ClientConfig{
			NamespaceId:         c.Config.Namespace, //namespace id
			TimeoutMs:           uint64(c.Config.TimeoutMs),
			NotLoadCacheAtStart: true,
			RotateTime:          c.Config.RotateTime,
			LogDir:              c.Config.LogDir,
			CacheDir:            c.Config.CacheDir,
			MaxAge:              c.Config.MaxAge,
			LogLevel:            c.Config.LogLevel,
		}
	})
}

func (c *Client) StartConfig() {
	c.initConfig()
	// 创建动态配置客户端
	configClient, err := clients.NewConfigClient(
		vo.NacosClientParam{
			ClientConfig:  &c.clientConfig,
			ServerConfigs: c.serverConfigs,
		},
	)

	if err != nil {
		log.Panic(err)
		return
	}

	c.configClient = configClient
}

func (c *Client) StartNaming() {
	c.initConfig()
	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &c.clientConfig,
			ServerConfigs: c.serverConfigs,
		},
	)

	if err != nil {
		log.Panic(err)
		return
	}

	c.namingClient = client

	for {

		if !c.doRegister() {
			log.Println("注册失败，等待5秒重试")
			time.Sleep(time.Second * 5)
		} else {
			log.Println("注册成功")
			break
		}
	}
}

func (c *Client) GetConfig() string {
	content, err := c.configClient.GetConfig(vo.ConfigParam{
		DataId: c.Config.ConfigDataId,
		Group:  c.Config.ConfigGroup,
	})
	if err != nil {
		log.Panic("读取配置中心数据出错", err)
	}
	fmt.Println(content)
	return content
}

func (c *Client) doRegister() bool {
	success, err := c.namingClient.RegisterInstance(vo.RegisterInstanceParam{
		Ip:          c.Config.ip,
		Port:        uint64(c.Config.Port),
		ServiceName: c.Config.ServiceName,
		GroupName:   c.Config.GroupName,
		Weight:      10,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
	})
	if err != nil {
		log.Println(err)
	}

	return success
}

func (c *Client) doUnRegister() bool {
	success, err := c.namingClient.DeregisterInstance(vo.DeregisterInstanceParam{
		Ip:          c.Config.ip,
		Port:        uint64(c.Config.Port),
		ServiceName: c.Config.ServiceName,
		GroupName:   c.Config.GroupName,
		Ephemeral:   true,
	})
	if err != nil {
		log.Println(err)
	}

	return success
}

func (c *Client) GetService(serviceName string) (model.Service, error) {

	return c.namingClient.GetService(vo.GetServiceParam{
		ServiceName: serviceName,
	})

}

// handleSignal 监听退出信号，删除注册的实例
func (c *Client) handleSignal() {
	if c.signalChan == nil {
		c.signalChan = make(chan os.Signal)
	}
	signal.Notify(c.signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
	for {
		switch <-c.signalChan {
		case syscall.SIGINT:
			fallthrough
		case syscall.SIGKILL:
			fallthrough
		case syscall.SIGTERM:
			log.Println("收到退出信号，解除注册")
			if !c.doUnRegister() {
				log.Println("解除注册失败")
			} else {
				log.Println("解除注册成功")
			}
			os.Exit(0)
		}
	}
}

// NewClient 创建客户端
func NewClient(config *Config) *Client {
	defaultConfig(config)
	return &Client{Config: config}
}

func defaultConfig(config *Config) {
	if config.ip == "" {
		config.ip = getLocalIP()
	}
	if config.Port == 0 {
		config.Port = 8848
	}
	if config.Namespace == "" {
		config.Namespace = constant.DEFAULT_NAMESPACE_ID
	}
	if config.GroupName == "" {
		config.GroupName = constant.DEFAULT_GROUP
	}
	if config.ServiceName == "" {
		config.ServiceName = "server"
	} else {
		config.ServiceName = strings.ToLower(config.ServiceName)
	}
	if config.LogDir == "" {
		config.LogDir = "/tmp/nacos/log"
	}
	if config.CacheDir == "" {
		config.CacheDir = "/tmp/nacos/cache"
	}
	if config.TimeoutMs == 0 {
		config.TimeoutMs = 5000
	}
	if config.MaxAge == 0 {
		config.MaxAge = 3
	}
	if config.LogLevel == "" {
		config.LogLevel = "debug"
	}
	if config.RotateTime == "" {
		config.RotateTime = "1h"
	}
}

func getLocalIP() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return ""
	}
	for _, address := range addrs {
		// check the address type and if it is not a loopback the display it
		if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	panic("Unable to get the local IP address")
}
